package com.qianxun.spider.util;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.*;

/**
 * 对象工具
 *
 * @author: huwei
 * @date: 2019/12/30 15:56
 * @version: 1.0.0
 */
public class ObjectUtil {
    /**
     * 转换为JsonObject
     *
     * @param json json字符串
     * @return
     */
    static public JsonObject toJsonObject(String json) {
        return new JsonParser().parse(json).getAsJsonObject();
    }

    /**
     * 转换为JsonArray
     *
     * @param json json字符串
     * @return
     */
    static public JsonArray toJsonArray(String json) {
        return new JsonParser().parse(json).getAsJsonArray();
    }

    /**
     * 将给定对象转换为json字符串
     *
     * @param obj 转换对象
     * @return 成功：转换后的字符串；失败：null
     */
    public static String toJson(Object obj) {
        return GsonFormatBuilder.defaultFormatGson().toJson(obj);
    }

    /**
     * 将指定json数据转换为指定的class对象
     *
     * @param json json数据
     * @param cls  转换对象
     * @param <T>  转换对象类型
     * @return 成功：返回转换后的对象； 失败： 返回null
     */
    public static <T> T toBean(byte[] json, Class<? extends T> cls) {
        return toBean(new String(json), cls);
    }

    /**
     * 将指定json数据转换为指定的class对象
     *
     * @param json    json数据
     * @param cls     转换对象
     * @param <T>     转换对象类型
     * @param charset 字符集
     * @return 成功：返回转换后的对象； 失败： 返回null
     */
    public static <T> T toBean(byte[] json, Charset charset, Class<? extends T> cls) {
        try {
            return toBean(new String(json, charset.name()), cls);
        } catch (UnsupportedEncodingException e) {
        }
        return toBean(json, cls);
    }

    /**
     * 将指定json数据转换为指定的class对象
     *
     * @param json json数据
     * @param cls  转换对象
     * @param <T>  转换对象类型
     * @return 成功：返回转换后的对象； 失败： 返回null
     */
    public static <T> T toBean(String json, Class<? extends T> cls) {
        try {
            return GsonFormatBuilder.defaultFormatGson().fromJson(json, cls);
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 将指定对象与指定key形成map映射关系
     *
     * @param key 指定key
     * @param obj 指定对象
     * @return map列表
     */
    public static Map<String, Object> toMap(String key, Object obj) {
        Map<String, Object> res = new HashMap<String, Object>(1);
        res.put(key, obj);
        return res;
    }

    /**
     * 将对象转换为字段名到字段值的map映射
     *
     * @param obj 转换对象
     * @return map列表
     */
    public static Map<String, Object> toMap(Object obj) {
        return toMapIn(obj);
    }

    /**
     * 将指定对象转换为字段名到字段值的map映射，但该映射列表不能包含后续指定参数的资源名的值映射
     *
     * @param obj           转换对象
     * @param excludeFields 排除字段，即转换后的map映射中key不能包含这些字段名
     * @return map列表
     */
    public static Map<String, Object> toMapEx(Object obj, String... excludeFields) {
        //获取对象map映射
        Map<String, Object> res = Map.class.isAssignableFrom(obj.getClass()) ? switchMap((Map<Object, Object>) obj) : objectToMap(obj);
        //排除字段
        if (!isEmpty(excludeFields)) {
            for (int i = 0; i < excludeFields.length; ++i) {
                res.remove(excludeFields[i]);
            }
        }
        return res;
    }

    /**
     * 将指定对象转换为字段名到字段值的map映射，但该映射列表中只能存在后续指定参数的字段名的值映射
     *
     * @param obj           转换对象
     * @param includeFields 排除字段，即转换后的map映射中key不能包含这些字段名
     * @return map列表
     */
    public static Map<String, Object> toMapIn(Object obj, String... includeFields) {
        //获取对象map映射
        Map<String, Object> res = Map.class.isAssignableFrom(obj.getClass()) ? switchMap((Map<Object, Object>) obj) : objectToMap(obj);
        //排除字段
        if (!isEmpty(includeFields)) {
            Map<String, Object> temp = new HashMap<>(includeFields.length);
            for (int i = 0; i < includeFields.length; ++i) {
                temp.put(includeFields[i], res.get(includeFields[i]));
            }
            res = temp;
        }
        return res;
    }

    /**
     * 获取目标对象指定字段名称的值
     *
     * @param target    目标对象
     * @param fieldName 字段名称
     * @return 字段值为null或目标对象不存在指定字段值或字段值不可访问，都将返回null ；反之返回对应的值；
     */
    public static Object getFieldValue(Object target, String fieldName) {
        Map<String, Object> map = toMap(target);
        return map == null ? null : map.get(fieldName);
    }

    /**
     * 为目标对象的指定字段设置值
     *
     * @param target    目标对象
     * @param fieldName 目标字段
     * @param value     设置值
     * @return 成功true ，失败false
     */
    public static void setFieldValue(Object target, String fieldName, Object value) {
        setFieldValue(target, getFieldsInSelf(target), fieldName, value);
    }

    /**
     * 获取给定对象的所有字段信息
     *
     * @param obj 给定对象
     * @return
     */
    public static List<Field> getFieldsInSelf(Object obj) {
        return getFieldsInSelf(obj.getClass());
    }

    /**
     * 获取当前类对象的所有字段信息
     *
     * @param cls 类对象
     * @return
     */
    public static List<Field> getFieldsInSelf(Class<?> cls) {
        List<Field> res = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();
        for (int i = 0; i < fields.length; ++i) {
            res.add(fields[i]);
        }
        return res;
    }

    /**
     * 获取给定对象所描述的继承链上所有的成员的字段信息
     *
     * @param obj 对象
     * @return
     */
    public static List<Field> getFieldsInFamily(Object obj) {
        return getFieldsInFamily(obj.getClass());
    }

    /**
     * 获取给定类对象所描述的继承链上所有的成员的字段信息
     *
     * @param cls 类对象
     * @return
     */
    public static List<Field> getFieldsInFamily(Class<?> cls) {
        List<Field> res = new ArrayList<>();
        List<Class<?>> clsList = Arrays.asList(cls);
        do {
            clsList.forEach(x -> {
                res.addAll(getFieldsInSelf(x));
            });
        } while (!(clsList = getAllParentClass(clsList)).isEmpty());
        return res;
    }

    /**
     * 将目标对象及其继承链对象字段的值为空的字段的值转换为源对象的相同字段名的值
     *
     * @param target 目标对象，即字段值将被修改的对象
     * @param src    源对象，即字段值获取对象
     */
    public static void switchNullFieldValue(Object target, Object src) {
        switchFieldValue(target, src, true);
    }

    /**
     * 创建指定对象，并为其赋值
     *
     * @param src 源对象，即字段值获取对象
     */
    public static <T> T switchAllFieldValue(Object src, Class<? extends T> cls) {
        T t = null;
        try {
            t = cls.getConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("指定对象构建方法失败，请求确保其由可用[公共无参]构造", e);
        }
        switchFieldValue(t, src, false);
        return t;
    }

    /**
     * 将目标对象及其继承链对象的所有字段值转换为源对象的相同字段名的值
     *
     * @param target 目标对象，即字段值将被修改的对象
     * @param src    源对象，即字段值获取对象
     */
    public static void switchAllFieldValue(Object target, Object src) {
        switchFieldValue(target, src, false);
    }

    /**
     * 将目标对象及其继承链对象字段值转换为源对象的相同字段名的值
     *
     * @param target      目标对象，即字段值将被修改的对象
     * @param src         源对象，即字段值获取对象
     * @param isNullField 空字段值标识，表示：是否只转换目标对象字段值为空的字段； true： 是 ，false ： 否，全部转换
     */
    public static void switchFieldValue(Object target, Object src, boolean isNullField) {
        List<Field> fieldList = getFieldsInFamily(target);
        Map<String, Object> srcFieldMap = toMap(src);
        for (Map.Entry<String, Object> entry : srcFieldMap.entrySet()) {
            if (isNullField && !isNull(getFieldValue(target, entry.getKey()))) {
                //只设置字段值为空的字段，但该字段值不为空，跳过
                continue;
            }
            Object value = entry.getValue();
            if (!isNull(value)) {
                setFieldValue(target, fieldList, entry.getKey(), value);
            }
        }
    }

    /**
     * 检索对象的所有的字段是否都为null
     *
     * @param obj 检索对象
     * @return true 是，false 否
     */
    public static boolean isFieldNull(Object obj) {
        return isFieldNull(obj, false);
    }

    /**
     * 检索对象所指定的字段是否为空，若检索对象不存在指定字段，则该字段不作为结果依据
     *
     * @param obj        检索对象
     * @param fieldNames 检索字段名
     * @return 指定的所有字段都为null时，返回true，反之false
     */
    public static boolean isFieldNullIn(Object obj, String... fieldNames) {
        return isFieldNull(obj, true, fieldNames);
    }

    /**
     * 检索对象的字段是否为空，指定排除的字段不做判断
     *
     * @param obj        检索对象
     * @param fieldNames 排除字段名
     * @return 排除字段外的字段值都为null时，返回true，反之false
     */
    public static boolean isFieldNullEx(Object obj, String... fieldNames) {
        return isFieldNull(obj, false, fieldNames);
    }

    /**
     * 检索对象是否为空
     *
     * @param obj 检索对象
     * @return
     */
    public static boolean isNull(Object obj) {
        return obj == null;
    }

    /**
     * 检索对象是否非空
     *
     * @param obj 检索对象
     * @return
     */
    public static boolean notNull(Object obj) {
        return !isNull(obj);
    }

    /**
     * 检索对象是否为空，或字符串为空串(String.trim().length() == 0)
     *
     * @param obj 检索对象
     * @return
     */
    public static boolean isBlank(Object obj) {
        return isNull(obj) || (String.class.isAssignableFrom(obj.getClass()) && ((String) obj).trim().length() == 0);
    }

    /**
     * 检索对象是否不为空，且不为空串
     *
     * @param obj
     * @return
     */
    public static boolean notBlank(Object obj) {
        return !isBlank(obj);
    }

    /**
     * 检索对象是否为空，或字符串为空串(String.trim().length() == 0) ，数组对象为空数组(Object[].length == 0) ，集合对象为空(Collection.isEmpty() 为true) ，map对象为空(Map.isEmpty() 为true)
     *
     * @param obj
     * @return
     */
    public static boolean isEmpty(Object obj) {
        boolean b = isBlank(obj) || (Collection.class.isAssignableFrom(obj.getClass()) && ((Collection) obj).isEmpty())
                || (Map.class.isAssignableFrom(obj.getClass()) && ((Map) obj).isEmpty());
        if (!b && obj.getClass().isArray()) {
            try {
                b = ((Object[]) obj).length == 0;
            } catch (ClassCastException e) {
                b = isEmptyForBaseStructArray(obj);
            }
        }
        return b;
    }

    /**
     * 检索对象是否不为空，且不为空串，数组、集合类型不为空（元素数量大于0）
     *
     * @param obj 检索对象
     * @return
     */
    public static boolean notEmpty(Object obj) {
        return !isEmpty(obj);
    }

    /**
     * 基本数据类型数组，为空检测
     *
     * @param obj 检索对象
     */
    private static boolean isEmptyForBaseStructArray(Object obj) {
        if (byte[].class.isAssignableFrom(obj.getClass()) && ((byte[]) obj).length == 0) {
            return true;
        } else if (short[].class.isAssignableFrom(obj.getClass()) && ((short[]) obj).length == 0) {
            return true;
        } else if (int[].class.isAssignableFrom(obj.getClass()) && ((int[]) obj).length == 0) {
            return true;
        } else if (long[].class.isAssignableFrom(obj.getClass()) && ((long[]) obj).length == 0) {
            return true;
        } else if (double[].class.isAssignableFrom(obj.getClass()) && ((double[]) obj).length == 0) {
            return true;
        } else if (float[].class.isAssignableFrom(obj.getClass()) && ((float[]) obj).length == 0) {
            return true;
        } else if (char[].class.isAssignableFrom(obj.getClass()) && ((char[]) obj).length == 0) {
            return true;
        } else if (boolean[].class.isAssignableFrom(obj.getClass()) && ((boolean[]) obj).length == 0) {
            return true;
        }
        return false;
    }

    /**
     * 将给定对象及其所有父类对象的所有字段信息转换为字段名到字段值的映射关系
     *
     * @param obj 指定对象
     * @return map列表
     */
    private static Map<String, Object> objectToMap(Object obj) {
        Map<String, Object> res = new HashMap<String, Object>(16);
        getFieldsInFamily(obj).forEach(field -> {
            field.setAccessible(true);
            Object value = null;
            try {
                value = field.get(obj);
            } catch (Exception e) {
            }
            res.put(field.getName(), value);
        });
        return res;
    }

    /**
     * 获取指定类类型列表的所有父类类型列表
     *
     * @param clsList 类类型列表
     * @return
     */
    private static List<Class<?>> getAllParentClass(List<Class<?>> clsList) {
        List<Class<?>> list = new ArrayList<>();
        clsList.forEach(x -> {
            Class<?> superclass = x.getSuperclass();
            if (superclass != null && !superclass.getName().equals(Object.class.getName())) {
                list.add(superclass);
            }
        });
        return list;
    }

    /**
     * 检索指定的值是否在给定列表中
     *
     * @param target 目标
     * @param array  指定集合
     * @return
     */
    private static boolean valueInArrays(String target, String[] array) {
        if (!isEmpty(target) && !isEmpty(array)) {
            for (int i = 0, len = array.length; i < len; ++i) {
                if (target.equals(array[i])) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检索对象的字段是否为null
     *
     * @param obj        检索对象
     * @param isInclude  字段标识，true时，表示只检索fieldNames参数表示的字段，反之，检索fieldNames参数表示字段之外的字段
     * @param fieldNames 字段列表
     * @return
     */
    private static boolean isFieldNull(Object obj, boolean isInclude, String... fieldNames) {
        Map<String, Object> map = toMap(obj);
        for (Map.Entry<String, Object> entity : map.entrySet()) {
            if (isInclude && valueInArrays(entity.getKey(), fieldNames) && !isNull(entity.getValue())) {
                return false;
            } else if (!isInclude && !valueInArrays(entity.getKey(), fieldNames) && !isNull(entity.getValue())) {
                return false;
            }
        }
        return true;
    }

    /**
     * 为目标对象的指定字段设置值
     *
     * @param target    目标对象
     * @param fieldList 目标对象的字段列表
     * @param fieldName 目标字段
     * @param value     设置值
     */
    private static void setFieldValue(Object target, List<Field> fieldList, String fieldName, Object value) {
        for (int i = 0; i < fieldList.size(); ++i) {
            Field field = fieldList.get(i);
            if (field.getName().equals(fieldName)) {
                Object tempValue = value;
                //类型不同，进行值适配
                if (!field.getType().isAssignableFrom(value.getClass())) {
                    Object adaptValue = adaptValue(field.getType(), value);
                    if (adaptValue == null) {
                        continue;
                    }
                    tempValue = adaptValue;
                }
                field.setAccessible(true);
                try {
                    field.set(target, tempValue);
                } catch (IllegalAccessException e) {
                }
            }
        }
    }

    /**
     * 对指定值进行byte ,short ,int ,long ,float 、double
     *
     * @param adaptiveClass 适配类型
     * @param value         原始值
     * @return 成功返回对应的值，失败null
     */
    private static Object adaptValue(Class<?> adaptiveClass, Object value) {
        try {
            //对byte ,short ,int ,long 进行适配
            if (adaptiveClass.isAssignableFrom(byte.class) || adaptiveClass.isAssignableFrom(Byte.class)) {
                return Byte.parseByte(value.toString());
            } else if (adaptiveClass.isAssignableFrom(short.class) || adaptiveClass.isAssignableFrom(Short.class)) {
                return Short.parseShort(value.toString());
            } else if (adaptiveClass.isAssignableFrom(int.class) || adaptiveClass.isAssignableFrom(Integer.class)) {
                return Integer.parseInt(value.toString());
            } else if (adaptiveClass.isAssignableFrom(long.class) || adaptiveClass.isAssignableFrom(Long.class)) {
                return Long.parseLong(value.toString());
            }
            //对float 、double适配
            if (adaptiveClass.isAssignableFrom(float.class) || adaptiveClass.isAssignableFrom(Float.class)) {
                return Float.parseFloat(value.toString());
            } else if (adaptiveClass.isAssignableFrom(double.class) || adaptiveClass.isAssignableFrom(Double.class)) {
                return Double.parseDouble(value.toString());
            }
            //对boolean适配
            if (adaptiveClass.isAssignableFrom(boolean.class) || adaptiveClass.isAssignableFrom(Boolean.class)) {
                if ("true".equalsIgnoreCase(value.toString())) {
                    return true;
                } else if ("false".equalsIgnoreCase(value.toString())) {
                    return false;
                }
            }
            //对char适配
            if (adaptiveClass.isAssignableFrom(char.class) || adaptiveClass.isAssignableFrom(Character.class)) {
                if (value.toString().length() == 1) {
                    return value.toString().charAt(0);
                }
            }
            //对时间类型适配
            if (adaptiveClass.isAssignableFrom(Date.class)) {
                Date date = null;
                try {
                    date = new Date(Date.parse(value.toString()));
                } catch (Exception e) {
                }
                if (date == null) {
                    List<String> dateFormatList = Arrays.asList("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yy-MM-dd HH:mm:ss", "yy-MM-dd", "yyyy年MM月dd HH:mm:ss"
                            , "yyyy年MM月dd", "yy年MM月dd HH:mm:ss", "yy年MM月dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd", "yy/MM/dd HH:mm:ss", "yy/MM/dd"
                            , "yyyy MM dd HH:mm:ss", "yyyy MM dd", "yy MM dd HH:mm:ss", "yy MM dd");
                    for (int i = 0; i < dateFormatList.size(); ++i) {
                        date = DateUtil.parse(value.toString(), dateFormatList.get(i), null);
                        if (date != null) {
                            break;
                        }
                    }
                }
                return date;
            }
            //对其它类型进行适配
            return toBean(toJson(value), adaptiveClass);
        } catch (Throwable e) {
            e.printStackTrace();
            //转换异常，不做处理
        }
        return null;
    }

    /**
     * 是否为本地对象的封装对象，即八大基本对象的封装对象
     *
     * @param cls 检索对象
     * @return
     */
    private static boolean isWrapperLocalClass(Class<?> cls) {
        return Byte.class.getName().equals(cls.getName()) || Short.class.getName().equals(cls.getName()) || Integer.class.getName().equals(cls.getName())
                || Long.class.getName().equals(cls.getName()) || Float.class.getName().equals(cls.getName()) || Double.class.getName().equals(cls.getName())
                || Character.class.getName().equals(cls.getName()) || Boolean.class.getName().equals(cls.getName());
    }

    /**
     * 将Object类型转换为String类型的map
     *
     * @param map 转换map
     * @return
     */
    private static Map<String, Object> switchMap(Map<Object, Object> map) {
        final Map<String, Object> res = new HashMap<>(map.size());
        map.forEach((k, v) -> {
            res.put(String.valueOf(k), v);
        });
        return res;
    }
}